#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static FILE *__stdin = NULL;
static FILE *__stdout = NULL;
static FILE *__stderr = NULL;

static size_t __tty_stdin_read(FILE *f, uint8_t *buff, size_t size)
{
    return read(0, buff, size);
}

static size_t __tty_stdout_write(FILE *f, uint8_t *buff, size_t size)
{
    return write(1, buff, size);
}

static size_t __tty_stderr_write(FILE *f, uint8_t *buff, size_t size)
{
    return write(2, buff, size);
}

static size_t __tty_null_read(FILE *f, uint8_t *buff, size_t size)
{
    return 0;
}

static size_t __tty_null_write(FILE *f, uint8_t *buff, size_t size)
{
    return 0;
}

static fpos_t __tty_null_seek(FILE *f, fpos_t pos, int whence)
{
    return 0;
}

static size_t __tty_null_close(FILE *f)
{
    return 0;
}

static size_t __file_read(FILE *f, uint8_t *buff, size_t size)
{
    return read(f->fd, buff, size);
}

static size_t __file_write(FILE *f, uint8_t *buff, size_t size)
{
    return write(f->fd, buff, size);
}

static fpos_t __file_seek(FILE *f, fpos_t off, int whence)
{
    return lseek(f->fd, off, whence);
}

static size_t __file_close(FILE *f)
{
    return close(f->fd);
}

FILE *__file_alloc(int fd)
{
    FILE *f;

    f = (FILE *)malloc(sizeof(FILE));
    if (!f)
        return 0;

    if (!fd) // stdin
    {
        f->fd = fd;
        f->read = __tty_stdin_read;
        f->write = __tty_null_write;
        f->seek = __tty_null_seek;
        f->close = __tty_null_close;

        f->fifo_read = fifo_alloc(BUFFSZ);
        f->fifo_write = fifo_alloc(BUFFSZ);

        f->buff = malloc(BUFFSZ);
        f->buffsize = BUFFSZ;
        f->rwflush = &__stdio_no_flush;

        f->pos = 0;
        f->mode = _IOLBF;
        f->err = 0;
        f->eof = 0;
    }
    else
    {
        if (fd == 1) // stdout
        {
            f->fd = fd;

            f->read = __tty_null_read;
            f->write = __tty_stdout_write;
            f->seek = __tty_null_seek;
            f->close = __tty_null_close;

            f->fifo_read = fifo_alloc(BUFFSZ);
            f->fifo_write = fifo_alloc(BUFFSZ);

            f->buff = malloc(BUFFSZ);
            f->buffsize = BUFFSZ;
            f->rwflush = &__stdio_no_flush;

            f->pos = 0;
            f->mode = _IOLBF;
            f->err = 0;
            f->eof = 0;
        }
        else
        {
            if (fd == 2) // stderr
            {
                f->fd = fd;

                f->read = __tty_null_read;
                f->write = __tty_stderr_write;
                f->seek = __tty_null_seek;
                f->close = __tty_null_close;

                f->fifo_read = fifo_alloc(BUFFSZ);
                f->fifo_write = fifo_alloc(BUFFSZ);

                f->buff = malloc(BUFFSZ);
                f->buffsize = BUFFSZ;
                f->rwflush = &__stdio_no_flush;

                f->pos = 0;
                f->mode = _IONBF;
                f->err = 0;
                f->eof = 0;
            }
            else // genaral file
            {
                f->fd = fd;

                f->read = __file_read;
                f->write = __file_write;
                f->seek = __file_seek;
                f->close = __file_close;

                f->fifo_read = fifo_alloc(BUFFSZ);
                f->fifo_write = fifo_alloc(BUFFSZ);

                f->buff = malloc(BUFFSZ);
                f->buffsize = BUFFSZ;
                f->rwflush = &__stdio_no_flush;

                f->pos = 0;
                f->mode = _IOFBF;
                f->err = 0;
                f->eof = 0;
            }
        }
    }
    return f;
}

FILE *__stdio_get_stdin()
{
    if (!__stdin)
    {
        __stdin = __file_alloc(0);
    }
    return __stdin;
}

FILE *__stdio_get_stdout()
{
    if (!__stdout)
    {
        __stdout = __file_alloc(1);
    }
    return __stdout;
}

FILE *__stdio_get_stderr()
{
    if (!__stderr)
    {
        __stderr = __file_alloc(2);
    }
    return __stderr;
}
